/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package minijava.visitor;


import minijava.SintaxeAbstrata.*;

public class TypeCheckVisitor extends TypeDepthFirstVisitor {

    static Class currClass;
    static Method currMethod;
    static SymbolTable symbolTable;

    public TypeCheckVisitor(SymbolTable s){
	symbolTable = s;

    }



    @Override
  public Type visit(Plus n) {
    if (! (n.e1.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado esquerdo de um Plus tem que ser do tipo inteiro");
       System.exit(-1);
    }
    if (! (n.e2.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado direito de um Plus tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new IntegerType();
  }

    @Override
  public Type visit(Minus n) {
    if (! (n.e1.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado esquerdo de um Minus tem que ser do tipo inteiro");
       System.exit(-1);
    }
    if (! (n.e2.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado direito de um Minus tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new IntegerType();
  }

    @Override
  public Type visit(Times n) {
    if (! (n.e1.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado esquerdo de um Times tem que ser do tipo inteiro");
       System.exit(-1);
    }
    if (! (n.e2.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado direito de um Times tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new IntegerType();
  }

    @Override
    public Type visit(And n) {
    if (! (n.e1.accept(this) instanceof BooleanType) ) {
       System.out.println("Lado esquerdo de um AND tem que ser do tipo inteiro");
       System.exit(-1);
    }
    if (! (n.e2.accept(this) instanceof BooleanType) ) {
       System.out.println("Lado direito de um AND tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new BooleanType();
  }

    @Override
    public Type visit(LessThan n) {
    if (! (n.e1.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado esquerdo de um Less tem que ser do tipo inteiro");
       System.exit(-1);
    }
    if (! (n.e2.accept(this) instanceof IntegerType) ) {
       System.out.println("Lado direito de um Less tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new BooleanType();
  }
    
    @Override
  public Type visit(ArrayLookup n) {
    if (! (n.e1.accept(this) instanceof IntArrayType) ) {
       System.out.println("target de um ArrayLookup tem que ser do tipo IntArray");
       System.exit(-1);
    }
    if (! (n.e2.accept(this) instanceof IntegerType) ) {
       System.out.println("indice de um ArrayLookup tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new IntegerType();
  }

    @Override
  public Type visit(ArrayLength n) {
    if (! (n.e.accept(this) instanceof IntArrayType) ) {
       System.out.println("target de um ArrayLength tem que ser do tipo Array");
       System.exit(-1);
    }
    return new IntegerType();
  }


    @Override
  public Type visit(Call n) {

    if (! (n.e.accept(this) instanceof IdentifierType)){
        System.out.println("metodo "+ n.i.toString() + " chamado em estrutura que nao � classe ou Objeto.");
        System.exit(-1);
    }

    String mname = n.i.toString();
    String cname = ((IdentifierType) n.e.accept(this)).s;

    Method calledMethod = TypeCheckVisitor.symbolTable.getMethod(mname,cname);

    for ( int i = 0; i < n.el.size(); i++ ) {
        Type t1 =null;
        Type t2 =null;

        if (calledMethod.getParamAt(i)!=null)
            t1 = calledMethod.getParamAt(i).type();
        t2 = n.el.elementAt(i).accept(this);
        if (!TypeCheckVisitor.symbolTable.compareTypes(t1,t2)){
            System.out.println("Erro de tipagem em : " + cname+"." +mname);
            System.exit(-1);
        }
    }

    return TypeCheckVisitor.symbolTable.getMethodType(mname,cname);
  }

    @Override
  public Type visit(IntegerLiteral n) {
    return new IntegerType();
  }

    @Override
  public Type visit(True n) {
    return new BooleanType();
  }

    @Override
  public Type visit(False n) {
    return new BooleanType();
  }

    @Override
  public Type visit(IdentifierExp n) {
    return TypeCheckVisitor.symbolTable.getVarType(TypeCheckVisitor.currMethod,
                      TypeCheckVisitor.currClass,n.s);
  }

    @Override
  public Type visit(This n) {
      return TypeCheckVisitor.currClass.type();
  }

    @Override
  public Type visit(NewArray n) {

    if (! (n.e.accept(this) instanceof IntegerType) ) {
       System.out.println("Expressao NewArray tem que ser do tipo inteiro");
       System.exit(-1);
    }
    return new IntArrayType();
  }

    @Override
  public Type visit(NewObject n) {
    return new IdentifierType(n.i.s);
  }



}


